package com.liu.spring.framework.context;

import com.liu.spring.framework.annotation.*;
import com.liu.spring.framework.beans.BeanDefinition;
import com.liu.spring.framework.aware.BeanNameAware;
import com.liu.spring.framework.aware.BeanPostProcessor;
import com.liu.spring.framework.aware.InitializingBean;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author LIU
 * @version 1.0
 * @description: TODO
 * @date 2022/8/25 15:40
 * <p>
 * Bean的生命周期
 * <p>
 * Bean的生命周期具体指的就是一个Bean对象从创建到销毁的过程 ， 其生成步骤如下
 * <p>
 * 1.（扫描）
 * 找到需要创建的类 Spring会通过扫描Class得到BeanDefinition（Bean的定义）
 * <p>
 * 2.（推断构造方法）实例化bean
 * 通过默认的无参构造方法得到一个对象(但是如何一个类中有多个构造方法，Spring则会进行选择，这个叫做推断构造方法)
 * <p>
 * 3.（依赖注入）
 * 给加了@Autowired等注解的属性进行赋值
 * <p>
 * 4.（Aware回调）
 * 依赖注入后，Spring会判断该对象是否实现了BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口，
 * 如果实现了，就表示当前对象必须实现该接口中所定义的setBeanName()、setBeanClassLoader()、setBeanFactory()方法，
 * 那Spring就会调用这些方法并传入相应的参数
 * <p>
 * 5.（初始化前）
 * Aware回调后，Spring会判断该对象中是否存在某个方法被@PostConstruct注解了，如果存在，Spring会调用当前对象的此方法（初始化前）
 * <p>
 * 6.（初始化）
 * 紧接着，Spring会判断该对象是否实现了InitializingBean接口，如果实现了，就表示当前对象必须实现该接口中的afterPropertiesSet()方法，
 * 那Spring就会调用当前对象中的afterPropertiesSet()方法
 * <p>
 * 7.（初始化后）
 * 最后，Spring会判断当前对象需不需要进行AOP，如果不需要那么Bean就创建完了，
 * 如果需要进行AOP，则会进行动态代理并生成一个代理对象做为Bean
 */
public class LiuApplicationContext {
    private final Class<?> configClass;
    //bean定义信息
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //单例bean容器
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    //前置与后置处理器
    private final List<BeanPostProcessor> beanPostProcessorsList = new ArrayList<>();


    public LiuApplicationContext(Class<?> configClass) {
        this.configClass = configClass;

        //扫描 --> BeanDefinition  --> BeanDefinitionMap
        scanBeanDefinition(configClass);

        //实例化单例bean
        registerBean();
    }


    /**
     * 扫描 -> 得到一系列 BeanDefinition，放入 beanDefinitionMap
     *
     * @param configClass
     */
    private void scanBeanDefinition(Class configClass) {
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScan.value();
            path = path.replace(".", "/");  //  com/liu/demo

            ClassLoader classLoader = LiuApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            //绝对路径  D:\workspace\spring\out\production\spring\com\liu\demo
            File file = new File(resource.getFile());

            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File f : files) {

                    //D:\workspace\spring\out\production\spring\com\liu\demo\AppConfig.class
                    String fileName = f.getAbsolutePath();

                    if (fileName.endsWith(".class")) {

                        //com\liu\demo\AppConfig
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));

                        //com.liu.demo.AppConfig
                        className = className.replace("\\", ".");

                        Class<?> clazz = null;
                        try {
                            clazz = classLoader.loadClass(className);

                            //生产BeanDefinition对象
                            if (clazz.isAnnotationPresent(Component.class)) {

                                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
                                    beanPostProcessorsList.add(instance);
                                }

                                Component component = clazz.getAnnotation(Component.class);
                                String beanName = component.value();

                                if ("".equals(beanName)) {
                                    //获取类名，默认首字母小写
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                                }

                                //生成beanDefinition对象
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(clazz);

                                //判断是否单例
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scope = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scope.value());
                                } else {
                                    beanDefinition.setScope("singleton");
                                }

                                //bean的类型key
                                beanDefinitionMap.put(beanName, beanDefinition);
                            }
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }
        System.out.println("===========================================================");
    }


    /**
     * 实例化单例bean
     * UserService.clazz -----> 无参的构造方法 ------> 创建对象 --------> 依赖注入 ----> aware回调
     * ----> 初始化前（@PostConstruct） ----> 初始化（afterPropertiesSet） ----> 初始化后(AOP生成代理对象代理对象)
     * ----> 放入Map（单例池）---->bean对象
     */
    private void registerBean() {

        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {

            BeanDefinition beanDefinition = beanDefinitionEntry.getValue();
            String beanName = beanDefinitionEntry.getKey();

//            if (beanDefinition.isSingleton()) {
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }

        }
    }

    /**
     * 创建 Bean 的三个核心方法
     *      createBeanInstance：例化，即调用对象的构造方法实例化对象
     *      populateBean：填充属性，主要对 bean 的依赖属性注入（@Autowired）
     *      initializeBean：回到一些如initMethod，InitalizingBean等方法
     *
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        try {
            //反射实例化
            Object instance = createBeanInstance(beanName,beanDefinition);

            // 属性填充
            populateBean(beanDefinition, instance);

            // 生命周期
            instance = initializeBean(beanName, beanDefinition, instance);

            return instance;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 反射实例化 @TODO（推断构造方法）
     *  有且仅有一个@Autowired(required = true)注解标注的构造方法
     *  不存在@Autowired注解标注的构造方法，有唯一一个有参构造方法
     *  多个构造方法，默认无参
     */
    private Object createBeanInstance(String beanName, BeanDefinition beanDefinition) throws Exception {
        Class clazz = beanDefinition.getType();
        Object instance = null;
        // 默认使用无参构造
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
            if (constructor.getParameterCount() == 0){
                 instance = constructor.newInstance();
            }
        }

        return instance;
    }

    /**
     * 依赖注入 简单实现
     */
    private void populateBean(BeanDefinition beanDefinition, Object instance) throws IllegalAccessException {
        Class clazz = beanDefinition.getType();

        // 解析字段上的 Autowired
        for (Field field : clazz.getDeclaredFields()) {
            //类的字段是否有autowired注解
            if (field.isAnnotationPresent(Autowired.class)) {
                field.setAccessible(true);
                //注入实例
                field.set(instance, getBean(field.getName()));
            }

        }
    }

    /**
     *  初始化阶段，包含：Aware回调、初始化前、初始化、初始化后
     */
    private Object initializeBean(String beanName, BeanDefinition beanDefinition, Object instance) throws IllegalAccessException, InvocationTargetException {
        //AWare 回调
        if (instance instanceof BeanNameAware) {
            ((BeanNameAware) instance).setBeanName(beanName);
        }

        //初始化前
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorsList) {
            instance = beanPostProcessor.postProcessorBeforeInitialization(beanName, instance);
        }

        // 扫描@PostConstruct注解
        for (Method method : beanDefinition.getType().getDeclaredMethods()) {
            //遍历类的方法是否有PostConstruct注解
            if (method.isAnnotationPresent(PostConstruct.class)) {
                //调用初始化方法  @TODO 参数解析
                method.invoke(instance, null);
            }
        }

        //初始化
        if (instance instanceof InitializingBean) {
            ((InitializingBean) instance).afterPropertiesSet();
        }

        // TODO 执行 @Bean(initMethod = “myInit”) 指定的初始化方法（将初始化方法记录在 BeanDefinition 中）

        //初始化后 AOP  BeanPostProcessor 后置处理器
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorsList) {
            instance = beanPostProcessor.postProcessorAfterInitialization(beanName, instance);
        }
        return instance;
    }


    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition == null) {
            throw new NullPointerException("beanName:" + beanName + "不存在");
        } else {
//            if (beanDefinition.isSingleton()) {
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = singletonObjects.get(beanName);
                //bean不存在则创建bean
                if (bean == null) {
                    bean = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName, bean);
                }
                return bean;
            } else {
                //多例
                return createBean(beanName, beanDefinition);
            }
        }
    }

}
